/**
 *  \file    fff.h
 *
 *  \brief   Fast First in First out buffer
 *
 *  \author  Michael J. Hohmann <mjh@scientist.de> (initial design and coding)
 *  \author  Marcus Holland-Moritz <mhx@cpan.org>
 *
 *  Copyright (C) 2003 Michael J. Hohmann and Marcus Holland-Moritz.
 *
 *
 */

#ifndef __FFF_H__
#define __FFF_H__

#ifdef __cplusplus
extern "C" {
#endif

/*==========================================================================*/

#ifndef __DOXYGEN__
#  define FFF_REVISION    0
#  define FFF_VERSION     2
#  define FFF_SUBVERSION  0
#endif

#ifndef FFF_NO_DYN
#  if !defined FFF_malloc || !defined FFF_free
#    include <stdlib.h>
#    ifndef FFF_malloc
/**
 * memory allocation function for dynamic size fifos
 *
 *  \param size        bytes of memory to allocate
 */
#      define FFF_malloc(size)  malloc(size)
#    endif
#    ifndef FFF_free
/**
 * memory freeing function for dynamic size fifos
 *
 *  \param ptr         pointer to allocated memory
 */
#      define FFF_free(ptr)     free(ptr)
#    endif
#  endif
#endif

#ifndef FFF_COUNT_T
/**
 * fifo count type
 *
 *  must be big enough to store the maximum number of items in a fifo
 */
typedef unsigned int FFF_COUNT_T;
#endif

#ifndef __DOXYGEN__

#  ifndef FFF_NO_MAX_COUNT
#    define __FFF_UPDATE_MAX_COUNT(me)                                       \
            if( (me)._nobj > (me)._nmax )                                    \
              (me)._nmax = (me)._nobj;
#    define __FFF_SET_MAX_COUNT(me, val)   (me)._nmax = val;
#    define __FFF_MAX_COUNT_DECL           FFF_COUNT_T _nmax;
#    define __FFF_MAX_COUNT_INIT           , 0
#  else
#    define __FFF_UPDATE_MAX_COUNT(me)
#    define __FFF_SET_MAX_COUNT(me, val)
#    define __FFF_MAX_COUNT_DECL
#    define __FFF_MAX_COUNT_INIT
#  endif

#  ifndef FFF_NO_ITERATOR
#    define __FFF_ITERATOR_DECL            FFF_COUNT_T _iter;
#  else
#    define __FFF_ITERATOR_DECL
#  endif

#endif

/**
 * defines the fifo buffer struct
 *
 *  \param type        type of objects in fifo
 *
 *  \param size        number of objects in fifo
 *
 *  \hideinitializer
 */
#define FFF_STRUCT(type, size)                                               \
        struct {                                                             \
          volatile type* _pput;          /* write pointer  */                         \
          volatile type* _pget;          /*  read pointer  */                         \
          volatile FFF_COUNT_T _iput;    /* write index    */                         \
          volatile FFF_COUNT_T _iget;    /*  read index    */                         \
          volatile FFF_COUNT_T _nobj;    /* object count   */                         \
          volatile FFF_COUNT_T _imax;    /* max buf index  */                         \
          __FFF_MAX_COUNT_DECL  /* max fill count */                         \
          __FFF_ITERATOR_DECL   /* iterator       */                         \
          type _obuf[size];     /* object buffer  */                         \
        }

/**
 * allocates a fifo buffer on the stack
 *
 *  \param name        name of fifo
 *
 *  \param type        type of objects in fifo
 *
 *  \param size        number of objects in fifo
 *
 *  \hideinitializer
 */
#define FFF_ALLOC(name, type, size)    FFF_STRUCT(type, size) name

/**
 * allocate and initialize a fifo buffer
 *
 *  \param name        name of fifo
 *
 *  \param type        type of objects in fifo
 *
 *  \param size        number of objects in fifo
 *
 *  \hideinitializer
 */
#define FFF_CREATE(name, type, size)                                         \
        FFF_ALLOC(name, type, size) = { &name._obuf[0], &name._obuf[0],      \
                                        (size)-1, (size)-1, 0, (size)-1      \
                                        __FFF_MAX_COUNT_INIT }

/**
 * initialize a fifo buffer
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_INIT(me)                                                         \
        do {                                                                 \
          (me)._imax = sizeof (me)._obuf / sizeof (me)._obuf[0] - 1;         \
          __FFF_SET_MAX_COUNT(me, 0)                                         \
          FFF_FLUSH(me);                                                     \
        } while(0)

/**
 * reset fifo, all information will be deleted
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_FLUSH(me)                                                        \
        do {                                                                 \
          (me)._pput = (me)._pget = (me)._obuf;                              \
          (me)._iput = (me)._iget = (me)._imax;                              \
          (me)._nobj = 0;                                                    \
        } while(0)

/**
 * return true if fifo is empty
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_IS_EMPTY(me)           ((me)._nobj == 0)


#define FFF_GET_COUNT(me)          ((me)._nobj)


/**
 * return true if fifo is not empty
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_IS_NOT_EMPTY(me)       ((me)._nobj > 0)

/**
 * return true if fifo is full
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_IS_FULL(me)            ((me)._nobj > (me)._imax)

/**
 * return true if fifo is not full
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_IS_NOT_FULL(me)        ((me)._nobj <= (me)._imax)

/**
 * return pointer to the next free write buffer
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_TAKE_WRITE_BUFFER(me)  ((me)._pput)

/**
 * return pointer to the next free read buffer
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_TAKE_READ_BUFFER(me)   ((me)._pget)

/**
 * return number of objects currently stored in the fifo
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_GET_OBJECT_COUNT(me)   ((me)._nobj)

#ifndef FFF_NO_MAX_COUNT
/**
 * return the maximum number of objects in fifo
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_GET_MAX_COUNT(me)      ((me)._nmax)
#else
#define FFF_GET_MAX_COUNT(me)      0
#endif

/**
 * return fifo size
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_GET_SIZE(me)           ((me)._imax+1)

#ifndef __DOXYGEN__
/* this should not be used by your application */
#  define __FFF_RING_BUFFER(dir, me)                                         \
          do {                                                               \
            if( (me)._i##dir ) {                                             \
              (me)._i##dir--;                                                \
              (me)._p##dir++;                                                \
            }                                                                \
            else {                                                           \
              (me)._i##dir = (me)._imax;                                     \
              (me)._p##dir = (me)._obuf;                                     \
            }                                                                \
          } while(0)
#endif

/**
 * Release the current read buffer
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_RELEASE_READ_BUFFER(me)                                          \
        do {                                                                 \
          __FFF_RING_BUFFER(get, me);                                        \
          (me)._nobj--;                                                      \
        } while(0)

/**
 * Release the current write buffer
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#define FFF_RELEASE_WRITE_BUFFER(me)                                         \
        do {                                                                 \
          __FFF_RING_BUFFER(put, me);                                        \
          (me)._nobj++;                                                      \
          __FFF_UPDATE_MAX_COUNT(me)                                         \
        } while(0)

/**
 * Put an object into the fifo
 *
 *  \param me          name of fifo
 *
 *  \param obj         object to put in,
 *                     guaranteed to be evaluated only once
 *
 *  \hideinitializer
 */
#define FFF_PUT(me, obj)                                                     \
        do {                                                                 \
          *FFF_TAKE_WRITE_BUFFER( me ) = obj;                                \
          FFF_RELEASE_WRITE_BUFFER( me );                                    \
        } while(0)

/**
 * Get an object from the fifo
 *
 *  \param me          name of fifo
 *
 *  \param obj         place to store the object,
 *                     guaranteed to be evaluated only once
 *
 *  \hideinitializer
 */
#define FFF_GET(me, obj)                                                     \
        do {                                                                 \
          obj = *FFF_TAKE_READ_BUFFER( me );                                 \
          FFF_RELEASE_READ_BUFFER( me );                                     \
        } while(0)

#ifndef FFF_NO_DYN

/**
 * defines a buffer struct for dynamic size fifos
 *
 *  \param type        type of objects in fifo
 *
 *  \hideinitializer
 */
#  define FFF_DYN_STRUCT(type)  FFF_STRUCT(type, 1)

/**
 * allocate and initialize a dynamic size fifo
 *
 *  \param p_me        fifo pointer to initialize
 *
 *  \param size        number of objects in fifo
 *
 *  \hideinitializer
 */
#  define FFF_DYN_CREATE(p_me, size)                                         \
          do {                                                               \
            (p_me) = (void *) 0;                                             \
            (p_me) = FFF_malloc( (unsigned) &(p_me)->_obuf[size] );          \
            (p_me)->_imax = size-1;                                          \
            __FFF_SET_MAX_COUNT(*(p_me), 0)                                  \
            FFF_FLUSH(*(p_me));                                              \
          } while(0)

/**
 * delete a dynamic size fifo
 *
 *  \param p_me        fifo pointer
 *
 *  \hideinitializer
 */
#  define FFF_DYN_DELETE(p_me)  FFF_free(p_me)

#endif /* FFF_NO_DYN */

#ifndef FFF_NO_ITERATOR

/**
 * iterate through all objects currently stored in the fifo
 *
 *  \param p_obj       variable that will receive the object
 *                     pointer with each iteration
 *
 *  \param me          name of fifo
 *
 *  \hideinitializer
 */
#  define FFF_FOREACH( p_obj, me )                                           \
          for( p_obj = (me)._pget, (me)._iter = (me)._nobj;                  \
               (me)._iter;                                                   \
               p_obj = (p_obj) == &(me)._obuf[(me)._imax]                    \
                         ? &(me)._obuf[0] : (p_obj)+1, --(me)._iter          \
             )

#endif /* FFF_NO_ITERATOR */

/*==========================================================================*/

#ifdef __cplusplus
}
#endif

#endif /* _FFF_H_ */
